/* 						-*- coding: utf-8 -*-
 * Une version supra-minimale  de editline...
 * Chaque prompt est précédé d'un peit message et il n'y a pas  
 * d'édition de ligne du tout!!
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h> 
#include <stdbool.h>
#include "editline.h"
#include "history.h"

#include <stdio.h>
#include <setjmp.h>
#include <signal.h>

static int cursorPos = 0;
int length = 0;
char *buff = NULL;
const char *prompt = NULL;
char trash[4096];
struct termios term_attributes;
jmp_buf env;
void (*sigBackup)(int) = NULL;

// efface la ligne de commande
void clean()
{
    int i;

    printf("\033[u%s", prompt);
    for(i = 0; i < length + 1; i++)
        printf(" ");
}

// réaffiche la ligne de commande
void printPrompt()
{
    clean();
    printf("\033[u%s%s\033[u%s", prompt, buff, prompt);
    if(cursorPos > 0)
        printf("\033[%dC", cursorPos);
    fflush(stdout);
}

// gère le ctrl+c
void sigHandler(int s)
{
    clean();
    siglongjmp(env, 1);
}

void addchar(char *buff, char c)
{
    // décaler ce qui suit d'un charactère vers la droite, puis placer ce charactère
    memmove(&buff[cursorPos] + 1, &buff[cursorPos], strlen(buff) - cursorPos + 1);
    buff[cursorPos++] = c;
    length++;
}

int on_signal(char *buff, int numSig) {
    char *p;
    int i;

    // printf("valeur current Pos %d\n", currentPos);
    switch(numSig) {
        case 1: // Ctrl-A: aller au début de la ligne
            cursorPos = 0;
            break;
        case 2: // Ctrl-B: caractère précédent
            if(cursorPos > 0)
                cursorPos--;
            break;
        case 4: //Ctrl-D: effacer le caractère courant (sortie de polysh si la ligne est vide)
            if(length == 0)
            {
                term_attributes.c_lflag |= (ICANON | ISIG | INLCR | ECHO) ;
                tcsetattr(fileno(stdin), TCSANOW, &term_attributes);
                return -1;
            }
            else if(cursorPos < length)
            {
                // décaler et écraser le charactère à effacer
                memmove(&buff[cursorPos], &buff[cursorPos + 1], 10);
                length--;
            }
            break;
        case 5: // Ctrl-E: aller à la fin de la ligne    
            cursorPos = length;
            break;
        case 6: // Ctrl-F: caractère suivant
            if(cursorPos < length)
                cursorPos++;
            break;
        case 127:
        case 8: // Ctrl-H (Backspace) ou Delete: effacer le caractère précédent
            if(cursorPos > 0)
            {
                // décaler et écraser le charactère à effacer
                memmove(&buff[cursorPos - 1], &buff[cursorPos], length - cursorPos + 1);
                length--;
                cursorPos--;
            }
            break;
        case 11: // Ctrl-K: effacer jusqu'à la fin de la ligne*/
            // mémoriser puis effacer la suite
            strcpy(trash, &buff[cursorPos]);
            buff[cursorPos] = '\0';
            break;  
        case 12: // Ctrl-L: ré-afficher la ligne courante
            printf("\n");
            fflush(stdout);
            break;
        case 10:
        case 13: // Ctrl-M (Return): exécuter la ligne saisie
            printPrompt();
            printf("\n");
            return 1;
        case 14: // Ctrl-N: commande suivante de l'historique
            if((p = next_history()))
            {
                length = strlen(p);
                strcpy(buff, p);
            }
            break;
        case 16: // Ctrl-P: commande précédente de l'historique
            if((p = previous_history()))
            {
                length = strlen(p);
                strcpy(buff, p);
            }
            break;
        case 25: // Ctrl-Y: restituer le texte détruit par Ctrl-K
            for(i = 0; i < strlen(trash); i++)
                addchar(buff, trash[i]);
          break;
        case 27:
          {
              char c;
              c = fgetc(stdin);
              if(c != 91)
                  break;
              c = fgetc(stdin);

              switch(c)
              {
                  case 66: // flèche bas
                      on_signal(buff, 14);
                      break;
                  case 65: // flèche haut
                      on_signal(buff, 16);
                      break;
                  case 67: // flèche droite
                      on_signal(buff, 6);
                      break;
                  case 68: // flèche gauche
                      on_signal(buff, 2);
                      break;
              }
          }
          break;
    }

    return 0;
}

char *editline(char *buf, const int maxsize, const char *prom)
{
    int c;

    // init
    trash[0] = '\0';
    prompt = prom;
    buff = buf;

    // on configure le terminal pour qu'il soit canonique et etc
    tcgetattr(fileno(stdin), &term_attributes);
    term_attributes.c_lflag &= ~(ICANON | /*ISIG | */INLCR | ECHO) ;
    term_attributes.c_cc[VMIN] = 1;
    term_attributes.c_cc[VTIME] = 0;
    tcsetattr(fileno(stdin), TCSANOW, &term_attributes);


    printf("\033[s%s", prompt); // sauvegarde le curseur au prompt
    fflush(stdout);

    // met en place une gestion du ctrl+c
    sigBackup = signal(SIGINT, sigHandler);
    sigsetjmp(env, 1);

    // (re-)initialise
    buff[0] = '\0';
    length = 0;
    cursorPos = 0;

    // On lit charactère par charactère
    while(1)
    {
        // (ré-)afficher la la ligne de commande
        printPrompt();

        // attendre une touche
        if((c = fgetc(stdin)) == EOF)
            return NULL;

        if(c < 32 || c == 127)
        {
            clean();
            // combinaisons spéciales (Ctrl+...)
            int r = on_signal(buff, c);
            if(r > 0 || r < 0)
            {
                term_attributes.c_lflag |= (ICANON | ISIG | INLCR | ECHO) ;
                tcsetattr(fileno(stdin), TCSANOW, &term_attributes);
                signal(SIGINT, sigBackup);

                return r > 0 ? buff : NULL;
            }
        }
        else
        {
            addchar(buff, c);
        }
    }
}
